﻿// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.

using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;

namespace System.Windows.Controls
{
    /// <summary>
    /// Provides useful extensions to ScrollViewer instances.
    /// </summary>
    /// <QualityBand>Experimental</QualityBand>
    public static class MyScrollViewerEx
    {

        #region private attached double VerticalOffset
        /// <summary>
        /// Gets the value of the VerticalOffset attached property for a specified ScrollViewer.
        /// </summary>
        /// <param name="element">The ScrollViewer from which the property value is read.</param>
        /// <returns>The VerticalOffset property value for the ScrollViewer.</returns>
        [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "This is an attached property and is only intended to be set on ScrollViewer's")]
        private static double GetVerticalOffset(ScrollViewer element)
        {
            if (element == null)
            {
                throw new ArgumentNullException("element");
            }
            return (double)element.GetValue(VerticalOffsetProperty);
        }

        /// <summary>
        /// Sets the value of the VerticalOffset attached property to a specified ScrollViewer.
        /// </summary>
        /// <param name="element">The ScrollViewer to which the attached property is written.</param>
        /// <param name="value">The needed VerticalOffset value.</param>
        [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "This is an attached property and is only intended to be set on ScrollViewer's")]
        private static void SetVerticalOffset(ScrollViewer element, double value)
        {
            if (element == null)
            {
                throw new ArgumentNullException("element");
            }
            element.SetValue(VerticalOffsetProperty, value);
        }

        /// <summary>
        /// Identifies the VerticalOffset dependency property.
        /// </summary>
        private static readonly DependencyProperty VerticalOffsetProperty =
            DependencyProperty.RegisterAttached(
                "VerticalOffset",
                typeof(double),
                typeof(ScrollViewerExtensions),
                new PropertyMetadata(OnVerticalOffsetPropertyChanged));

        /// <summary>
        /// VerticalOffsetProperty property changed handler.
        /// </summary>
        /// <param name="dependencyObject">ScrollViewer that changed its VerticalOffset.</param>
        /// <param name="eventArgs">Event arguments.</param>
        private static void OnVerticalOffsetPropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs)
        {
            ScrollViewer source = dependencyObject as ScrollViewer;
            if (source == null)
            {
                throw new ArgumentNullException("dependencyObject");
            }

            source.ScrollToVerticalOffset((double)eventArgs.NewValue);
        }
        #endregion private attached double VerticalOffset

        #region private attached double HorizontalOffset
        /// <summary>
        /// Gets the value of the HorizontalOffset attached property for a specified ScrollViewer.
        /// </summary>
        /// <param name="element">The ScrollViewer from which the property value is read.</param>
        /// <returns>The HorizontalOffset property value for the ScrollViewer.</returns>
        [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "This is an attached property and is only intended to be set on ScrollViewer's")]
        private static double GetHorizontalOffset(ScrollViewer element)
        {
            if (element == null)
            {
                throw new ArgumentNullException("element");
            }
            return (double)element.GetValue(HorizontalOffsetProperty);
        }

        /// <summary>
        /// Sets the value of the HorizontalOffset attached property to a specified ScrollViewer.
        /// </summary>
        /// <param name="element">The ScrollViewer to which the attached property is written.</param>
        /// <param name="value">The needed HorizontalOffset value.</param>
        [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "This is an attached property and is only intended to be set on ScrollViewer's")]
        private static void SetHorizontalOffset(ScrollViewer element, double value)
        {
            if (element == null)
            {
                throw new ArgumentNullException("element");
            }
            element.SetValue(HorizontalOffsetProperty, value);
        }

        /// <summary>
        /// Identifies the HorizontalOffset dependency property.
        /// </summary>
        private static readonly DependencyProperty HorizontalOffsetProperty =
            DependencyProperty.RegisterAttached(
                "HorizontalOffset",
                typeof(double),
                typeof(ScrollViewerExtensions),
                new PropertyMetadata(OnHorizontalOffsetPropertyChanged));

        /// <summary>
        /// HorizontalOffsetProperty property changed handler.
        /// </summary>
        /// <param name="dependencyObject">ScrollViewer that changed its HorizontalOffset.</param>
        /// <param name="eventArgs">Event arguments.</param>
        private static void OnHorizontalOffsetPropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs)
        {
            ScrollViewer source = dependencyObject as ScrollViewer;
            if (source == null)
            {
                throw new ArgumentNullException("dependencyObject");
            }

            source.ScrollToHorizontalOffset((double)eventArgs.NewValue);
        }
        #endregion private attached double HorizontalOffset

 

        /// <summary>
        /// Scroll the desired element into the ScrollViewer's viewport.
        /// </summary>
        /// <param name="viewer">The ScrollViewer.</param>
        /// <param name="element">The element to scroll into view.</param>
        /// <param name="horizontalMargin">The margin to add on the left or right.
        /// </param>
        /// <param name="verticalMargin">The margin to add on the top or bottom.
        /// </param>
        /// <param name="duration">The duration of the animation.</param>
        /// <exception cref="T:System.ArgumentNullException">
        /// <paramref name="viewer" /> is null.
        /// </exception>
        /// <exception cref="T:System.ArgumentNullException">
        /// <paramref name="element" /> is null.
        /// </exception>
        public static void ScrollToCenter(this ScrollViewer viewer, double verticalOffset, double horizontalOffset, Duration duration)
        {
            if (viewer == null)
            {
                throw new ArgumentNullException("viewer");
            }
            else
            {
                Storyboard storyboard = new Storyboard();
                SetVerticalOffset(viewer, viewer.VerticalOffset);
                SetHorizontalOffset(viewer, viewer.HorizontalOffset);

                DoubleAnimation verticalOffsetAnimation = new DoubleAnimation { To = verticalOffset, Duration = duration };
                DoubleAnimation horizontalOffsetAnimation = new DoubleAnimation { To = horizontalOffset, Duration = duration };

                Storyboard.SetTarget(verticalOffsetAnimation, viewer);
                Storyboard.SetTarget(horizontalOffsetAnimation, viewer);
                Storyboard.SetTargetProperty(horizontalOffsetAnimation, new PropertyPath(MyScrollViewerEx.HorizontalOffsetProperty));
                Storyboard.SetTargetProperty(verticalOffsetAnimation, new PropertyPath(MyScrollViewerEx.VerticalOffsetProperty));

                storyboard.Children.Add(verticalOffsetAnimation);
                storyboard.Children.Add(horizontalOffsetAnimation);

                storyboard.Begin();
            }
        }
    }
}